/*
 * Created on Aug 4, 2006
 *
 */
package com.inibb.app.sftp;

import java.io.File;
import java.net.InetAddress;
import java.util.Hashtable;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.inibb.app.sftp.exception.SFTPException;
import com.inibb.app.sftp.exception.SFTPIOException;
import com.inibb.app.sftp.exception.SFTPLocalIOException;
import com.inibb.app.sftp.exception.SFTPRemoteFileNotFoundException;
import com.inibb.app.sftp.exception.SFTPRemoteIOException;




/**
 * @author PCCW
 *
 */
public class PooledSFTPConnection implements SFTPConnection {
	public static final int DEFAULT_MAX_TRY_COUNT = 2;
	public static final int DEFAULT_TRY_WAIT = 10000;
	
	private static final Logger LOG = LoggerFactory.getLogger(PooledSFTPConnection.class);
	private SFTPConnection realConn = null;
	private InetAddress addr = null;
	private int port = SFTPConnection.DEFAULT_SSH_PORT;
	private SFTPAuthInfo auth = null;
	
	private int maxTryCount = DEFAULT_MAX_TRY_COUNT;
	private int tryWait = DEFAULT_TRY_WAIT;
	
	private RemoteFileEntry realPwd = null;
	private boolean exclusive = false;
	private boolean disconnecting = false;
	private Object connectLock = new Object();
	private String nameMark = "";
	
	PooledSFTPConnection(SFTPConnection conn, InetAddress addr, int port, SFTPAuthInfo auth) {
		realConn = conn;
		this.addr = addr;
		this.port = port; 
		this.auth = auth;
		
	}
	
	void resumeConnection() 
		throws SFTPIOException, SFTPException 
	{
		checkConnection() ;
			
		if (!realConn.isConnected()) {
			synchronized (connectLock) {
				if (!realConn.isConnected()) {
					SFTPException sftpe=null;
					int count = 0;
					do {
						count++;
						
						try {
							realConn.connect(addr,port,auth);
							sftpe=null;
						} catch (SFTPException s) {
							sftpe = s;
						}
						
						if (realPwd!=null) {
							try {
								realConn.changeDir(realPwd);
							} catch(Exception e) {
								LOG.warn(nameMark+"Failed to resume the PWD of the SFTPConnection : "+e);
							}
						}
						
					} while (sftpe!=null && count<maxTryCount);
					if (sftpe!=null) throw sftpe;
				}
			}
		}
	}
	
	void graceDisconnect() {
		disconnecting = true;
	}
	
	void forceDisconnect() {
		disconnecting = true;
		//if (realConn.isConnected()) {
			try {
				realConn.disconnect();
			} catch (Exception e) {
				LOG.warn(nameMark+"Error when disconnecting : "+e);
			}
		//}
	}
	
	private void checkConnection() 
		throws SFTPIOException
	{
		if (disconnecting) throw new SFTPIOException(SFTPIOException.CODE_RUNTIME,nameMark+"Connection has been closed.");		
	}
	
	/* 
	 * @see com.pccw.oframework.sftp.SFTPConnection#connect(java.net.InetAddress, int, com.pccw.oframework.sftp.SFTPAuthInfo)
	 */
	public void connect(InetAddress addr, int port, SFTPAuthInfo auth)
		throws SFTPIOException, SFTPException {
		//Ignore
	}

	/* 
	 * @see com.pccw.oframework.sftp.SFTPConnection#connect(java.net.InetAddress, com.pccw.oframework.sftp.SFTPAuthInfo)
	 */
	public void connect(InetAddress addr, SFTPAuthInfo auth)
		throws SFTPIOException, SFTPException {
//		Ignore
	}

	/* 
	 * @see com.pccw.oframework.sftp.SFTPConnection#disconnect()
	 */
	public void disconnect() throws SFTPIOException, SFTPException {
		if (exclusive) {
			forceDisconnect();
		}
	}

	/* 
	 * @see com.pccw.oframework.sftp.SFTPConnection#list(com.pccw.oframework.sftp.RemoteFileEntry)
	 */
	public List list(RemoteFileEntry path)
		throws SFTPRemoteFileNotFoundException, SFTPRemoteIOException, SFTPIOException, SFTPException 
	{
		checkConnection() ;
		try {
			return realConn.list(path);
		} catch (SFTPIOException sioe) {
			resumeConnection() ;
			return realConn.list(path);
		}
	}

	/* 
	 * @see com.pccw.oframework.sftp.SFTPConnection#changeDir(java.lang.String)
	 */
	public RemoteFileEntry changeDir(String path)
		throws SFTPRemoteFileNotFoundException, SFTPRemoteIOException,SFTPIOException, SFTPException 
	{
		checkConnection() ;
		try {
			return realPwd=realConn.changeDir(path);
		} catch (SFTPIOException sioe) {
			resumeConnection() ;
			return realPwd=realConn.changeDir(path);
		}
	}

	/* 
	 * @see com.pccw.oframework.sftp.SFTPConnection#changeDir(com.pccw.oframework.sftp.RemoteFileEntry)
	 */
	public RemoteFileEntry changeDir(RemoteFileEntry path)
		throws SFTPRemoteFileNotFoundException, SFTPRemoteIOException, SFTPIOException, SFTPException 
	{
		checkConnection() ;
		try {
			return realPwd=realConn.changeDir(path);
		} catch (SFTPIOException sioe) {
			resumeConnection() ;
			return realPwd=realConn.changeDir(path);
		}
	}

	/* 
	 * @see com.pccw.oframework.sftp.SFTPConnection#pwd()
	 */
	public RemoteFileEntry pwd() 
		throws SFTPRemoteIOException, SFTPIOException, SFTPException 
	{
		checkConnection() ;
		try {
			return realConn.pwd();
		} catch (SFTPIOException sioe) {
			resumeConnection() ;
			return realConn.pwd();
		}
	}

	/* 
	 * @see com.pccw.oframework.sftp.SFTPConnection#remove(com.pccw.oframework.sftp.RemoteFileEntry)
	 */
	public void remove(RemoteFileEntry file)
		throws SFTPRemoteFileNotFoundException, SFTPRemoteIOException,SFTPIOException, SFTPException 
	{
		checkConnection() ;
		try {
			realConn.remove(file);
		} catch (SFTPIOException sioe) {
			resumeConnection() ;
			realConn.remove(file);
		}
	}

	/* 
	 * @see com.pccw.oframework.sftp.SFTPConnection#removeDir(com.pccw.oframework.sftp.RemoteFileEntry)
	 */
	public void removeDir(RemoteFileEntry dir)
		throws SFTPRemoteFileNotFoundException, SFTPRemoteIOException, SFTPIOException, SFTPException 
	{
		checkConnection() ;
		try {
			realConn.removeDir(dir);
		} catch (SFTPIOException sioe) {
			resumeConnection() ;
			realConn.removeDir(dir);
		}
	}

	/* 
	 * @see com.pccw.oframework.sftp.SFTPConnection#rename(com.pccw.oframework.sftp.RemoteFileEntry, com.pccw.oframework.sftp.RemoteFileEntry)
	 */
	public void rename(RemoteFileEntry source, RemoteFileEntry target)
		throws SFTPRemoteFileNotFoundException, SFTPRemoteIOException,SFTPIOException, SFTPException 
	{
		checkConnection() ;
		try {
			realConn.rename(source,target);
		} catch (SFTPIOException sioe) {
			resumeConnection() ;
			realConn.rename(source,target);
		}
	}

	/* 
	 * @see com.pccw.oframework.sftp.SFTPConnection#mkdir(com.pccw.oframework.sftp.RemoteFileEntry)
	 */
	public void mkdir(RemoteFileEntry dir) 
		throws SFTPRemoteIOException,SFTPIOException, SFTPException 
	{
		checkConnection() ;
		try {
			realConn.mkdir(dir);
		} catch (SFTPIOException sioe) {
			resumeConnection() ;
			realConn.mkdir(dir);
		}
	}

	/* 
	 * @see com.pccw.oframework.sftp.SFTPConnection#put(java.io.File, com.pccw.oframework.sftp.RemoteFileEntry)
	 */
	public void put(File source, RemoteFileEntry dest)
		throws SFTPLocalIOException, SFTPRemoteIOException, SFTPIOException, SFTPException 
	{
		checkConnection() ;
		try {
			realConn.put(source,dest);
		} catch (SFTPIOException sioe) {
			resumeConnection() ;
			realConn.put(source,dest);
		}
	}

	/* 
	 * @see com.pccw.oframework.sftp.SFTPConnection#get(com.pccw.oframework.sftp.RemoteFileEntry, java.io.File)
	 */
	public void get(RemoteFileEntry source, File dest)
		throws SFTPLocalIOException, SFTPRemoteFileNotFoundException,SFTPRemoteIOException, SFTPIOException, SFTPException 
	{
		checkConnection() ;
		try {
			realConn.get(source,dest);
		} catch (SFTPIOException sioe) {
			resumeConnection() ;
			realConn.get(source,dest);
		}
	}

	/* 
	 * @see com.pccw.oframework.sftp.SFTPConnection#isConnected()
	 */
	public boolean isConnected() {
		return realConn.isConnected();
	}

	
	/**
	 * @return Returns the realConn.
	 */
	SFTPConnection getRealConn() {
		return realConn;
	}
	/**
	 * @param realConn The realConn to set.
	 */
	void setRealConn(SFTPConnection realConn) {
		this.realConn = realConn;
	}
	
	/**
	 * @return Returns the addr.
	 */
	InetAddress getAddr() {
		return addr;
	}
	/**
	 * @param addr The addr to set.
	 */
	void setAddr(InetAddress addr) {
		this.addr = addr;
	}
	/**
	 * @return Returns the auth.
	 */
	SFTPAuthInfo getAuth() {
		return auth;
	}
	/**
	 * @param auth The auth to set.
	 */
	void setAuth(SFTPAuthInfo auth) {
		this.auth = auth;
	}
	/**
	 * @return Returns the port.
	 */
	int getPort() {
		return port;
	}
	/**
	 * @param port The port to set.
	 */
	void setPort(int port) {
		this.port = port;
	}

	/**
	 * @return Returns the tryCount.
	 */
	int getMaxTryCount() {
		return maxTryCount;
	}
	/**
	 * @param tryCount The tryCount to set.
	 */
	void setMaxTryCount(int tryCount) {
		this.maxTryCount = tryCount;
	}

	/**
	 * @return Returns the tryWait.
	 */
	int getTryWait() {
		return tryWait;
	}
	/**
	 * @param tryWait The tryWait to set.
	 */
	void setTryWait(int tryWait) {
		this.tryWait = tryWait;
	}
	/**
	 * @return Returns the exclusive.
	 */
	boolean isExclusive() {
		return exclusive;
	}
	/**
	 * @param exclusive The exclusive to set.
	 */
	void setExclusive(boolean exclusive) {
		this.exclusive = exclusive;
	}
	String getNameMark() {
		return nameMark;
	}
	void setNameMark(String nameMark) {
		this.nameMark = nameMark;
	}

	/* 
	 * @see com.pccw.oframework.sftp.SFTPConnection#setConfig(java.util.Hashtable)
	 */
	public void setConfig(Hashtable config) {
		// Ignore
		
	}

	/* 
	 * @see com.pccw.oframework.sftp.SFTPConnection#setNoneCipherMode(boolean)
	 */
	public void setNoneCipherMode(boolean b) {
		// Ignore
		
	}
	
	protected void finalize() throws SFTPIOException, SFTPException {
		if (realConn!=null) {
			realConn.disconnect();
		}
	}
}
